package uk.co.hs.web.security.voter;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;

import uk.co.hs.service.admin.interfaces.WebUserServiceInterface;
import uk.co.hs.web.interceptor.TabsAccessibilityInterceptor;
import uk.co.hs.web.security.authentication.UtilisoftAuthentication;

public class UtilisoftRoleVoter extends RoleVoter
{
  @Autowired(required=true)
  @Qualifier("hs_webUserService")
  private WebUserServiceInterface mUserService;

  private ConcurrentMap<String, String> mAllUserRoles;
  private ConcurrentMap<String, ConcurrentMap<String, Boolean>> mAllUsersAllowedUrls;
  private boolean mSingleSignOn;

  /**
   * Returns 1 if user is allowed access to the URL. 0 if not.
   *
   * @see org.springframework.security.access.vote.RoleVoter
   * #vote(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
   * {@inheritDoc}
   */
  public int vote(Authentication aAuthentication,
  		            Object aFilterInvocation,
  		            Collection<ConfigAttribute> aCollection)
  {
    synchronized (UtilisoftRoleVoter.class)
    {
      FilterInvocation filterInvocation = (FilterInvocation) aFilterInvocation;
      String url = getShortURL(filterInvocation.getRequestUrl());

      if (automcatiallyAllowUrl(url, aAuthentication.getName()))
      {
        return 1;
      }

      // Only reload the whole user URL structure if someone has navigated to this page.
      if (url.equals(TabsAccessibilityInterceptor.ROLE_LIST_URL)
          || url.equals(TabsAccessibilityInterceptor.USER_LIST_URL)
          || mAllUsersAllowedUrls == null)
      {
        loadAllUsersAllowedUrls();
      }

      return isAllowedAccess(aAuthentication, url);
    }
  }

  private Boolean automcatiallyAllowUrl(String aURL,
                                        String aUsername)
  {
    // Forwarding URL always allowed
    String strippedURL = aURL;
    if (strippedURL.equals(TabsAccessibilityInterceptor.FORWARDING_URL))
    {
      return Boolean.TRUE;
    }

    return Boolean.FALSE;
  }

  /**
   * Is the user allowed access to the page, and if it is add whether it has editable permission or not.
   * @param aAuthentication
   * @param aURL
   * @return
   */
  private int isAllowedAccess(Authentication aAuthentication, String aURL)
  {
    org.springframework.security.core.userdetails.User authenticatedUser;

    try
    {
      authenticatedUser = (org.springframework.security.core.userdetails.User) aAuthentication.getPrincipal();
    }
    catch (Exception e)
    {
      authenticatedUser = null;
    }

    if (authenticatedUser != null)
    {
      String username = authenticatedUser.getUsername();

      Map<String, Boolean> allowedURLS = mAllUsersAllowedUrls.get(username);
      Collection<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<SimpleGrantedAuthority>();

      if (mAllUserRoles.get(username) != null)
      {
        grantedAuthorities.add(new SimpleGrantedAuthority(mAllUserRoles.get(username)));
      }

      if (!mSingleSignOn)
      {
        grantedAuthorities.add(new SimpleGrantedAuthority("LOGON_NOSSO"));
      }
      else
      {
        grantedAuthorities.add(new SimpleGrantedAuthority("LOGON_SSO"));
      }

      String strippedURL = aURL;
      if (allowedURLS != null && allowedURLS.containsKey(strippedURL))
      {
        grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ACCESS_GRANTED"));

        if (allowedURLS.get(strippedURL))
        {
          grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_EDITABLE"));
        }

        UtilisoftAuthentication utilisoftAuthentication = new UtilisoftAuthentication(aAuthentication, allowedURLS);
        utilisoftAuthentication.setAuthorities(grantedAuthorities);
        SecurityContextHolder.getContext().setAuthentication(utilisoftAuthentication);
        return 1;
      }

      UtilisoftAuthentication utilisoftAuthentication = new UtilisoftAuthentication(aAuthentication, allowedURLS);
      utilisoftAuthentication.setAuthorities(grantedAuthorities);
      SecurityContextHolder.getContext().setAuthentication(utilisoftAuthentication);
      return 0;
    }
    return 0;
  }

  private String getShortURL(String aLongURL)
  {
    int position = aLongURL.indexOf(".htm");

    return aLongURL.substring(1, position + 4);
  }

  private void loadAllUsersAllowedUrls()
  {
    mAllUsersAllowedUrls = mUserService.getAllUsersAllowedUrls();
    mAllUserRoles = mUserService.getAllUserRoles();
  }

  public void setSingleSignOn(boolean aIsSingleSignOn)
  {
    mSingleSignOn = aIsSingleSignOn;
  }
}
